<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
   
  <meta name="keywords" content="Java Python" />
   
  <meta name="description" content="From Zero to Hero" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    Linux-02-进程通信方式 |  朱酱酱的学习博客
  </title>
  <meta name="generator" content="hexo-theme-yilia-plus">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/css/style.css">

  
<script src="/js/pace.min.js"></script>


  

  

<link rel="alternate" href="/atom.xml" title="朱酱酱的学习博客" type="application/atom+xml">
</head>

</html>

<body>
  <div id="app">
    <main class="content">
      <section class="outer">
  <article id="post-Linux/Linux-02-进程通信方式" class="article article-type-post" itemscope
  itemprop="blogPost" data-scroll-reveal>

  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  Linux-02-进程通信方式
</h1>
  

    </header>
    

    
    <div class="article-meta">
      <a href="/2020/08/25/Linux/Linux-02-%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F/" class="article-date">
  <time datetime="2020-08-25T07:52:53.000Z" itemprop="datePublished">2020-08-25</time>
</a>
      
      
      
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">7.9k字</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">33分钟</span>
        </span>
    </span>
</div>

      
    </div>
    

    
    
    <div class="tocbot"></div>





    

    <div class="article-entry" itemprop="articleBody">
      


      

      
      <h1 id="Linux-02-进程通信方式"><a href="#Linux-02-进程通信方式" class="headerlink" title="Linux-02-进程通信方式"></a>Linux-02-进程通信方式</h1><p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200927/102456602.png" alt="mark"></p>
<a id="more"></a>

<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><ul>
<li>每个进程的用户地址空间都是独立的，一般而言是不能互相访问的，但内核空间是每个进程都共享的，所以进程之间要通信必须通过内核。</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200927/102542062.png" alt="mark"></p>
<ul>
<li>Linux 内核提供了不少进程间通信的机制，我们来一起瞧瞧有哪些？</li>
</ul>
<h2 id="一、管道"><a href="#一、管道" class="headerlink" title="一、管道"></a>一、管道</h2><p>管道，通常指无名管道，是 UNIX 系统IPC最古老的形式。</p>
<h3 id="1、特点"><a href="#1、特点" class="headerlink" title="1、特点"></a>1、特点</h3><ol>
<li>它是半双工的（即数据只能在一个方向上流动），具有固定的读端和写端。</li>
<li>它只能用于具有<strong>亲缘关系的进程之间的通信（也是父子进程或者兄弟进程之间）</strong>。</li>
<li>它可以看成是一种特殊的文件，对于它的读写也可以使用普通的<code>read、write</code> 等函数。但是它不是普通的文件，并不属于其他任何文件系统，并且只存在于内存中。</li>
</ol>
<p>如果你学过 Linux 命令，那你肯定很熟悉「<code>|</code>」这个竖线。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ ps auxf | grep mysql</span><br></pre></td></tr></table></figure>

<p>上面命令行里的「<code>|</code>」竖线就是一个<strong>管道</strong>，它的功能是将前一个命令（<code>ps auxf</code>）的输出，作为后一个命令（<code>grep mysql</code>）的输入，从这功能描述，可以看出<strong>管道传输数据是单向的</strong>，如果想相互通信，我们需要创建两个管道才行。</p>
<p>同时，我们得知上面这种管道是没有名字，所以「<code>|</code>」表示的管道称为<strong>匿名管道</strong>，用完了就销毁。</p>
<ul>
<li>匿名管道的创建，需要通过下面这个系统调用：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">int pipe(int fd[2])</span><br></pre></td></tr></table></figure>

<p>这里表示创建一个匿名管道，并返回了两个描述符，一个是管道的读取端描述符<code>fd[0]</code>，另一个是管道的写入端描述符 <code>fd[1]</code>。注意，这个匿名管道是特殊的文件，只存在于内存，不存于文件系统中。</p>
<h3 id="2、原型"><a href="#2、原型" class="headerlink" title="2、原型"></a>2、原型</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">1 #include &lt;unistd.h&gt;</span><br><span class="line">2 int pipe(int fd[2]);    &#x2F;&#x2F; 返回值：若成功返回0，失败返回-1</span><br></pre></td></tr></table></figure>

<p><strong>当一个管道建立时，它会创建两个文件描述符</strong>：<code>fd[0]</code>为读而打开，<code>fd[1]</code>为写而打开。如下图：</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200927/103118008.png" alt="mark"></p>
<p>要关闭管道只需将这两个文件描述符关闭即可。</p>
<p>看到这，你可能会有疑问了，这两个描述符都是在一个进程里面，并没有起到进程间通信的作用，怎么样才能使得管道是跨过两个进程的呢？</p>
<p>我们可以使用 <code>fork</code> 创建子进程，<strong>创建的子进程会复制父进程的文件描述符</strong>，这样就做到了两个进程各有两个「 <code>fd[0]</code> 与 <code>fd[1]</code>」，两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。</p>
<p>管道只能一端写入，另一端读出，所以上面这种模式容易造成混乱，因为父进程和子进程都可以同时写入，也都可以读出。那么，为了避免这种情况，通常的做法是：</p>
<ul>
<li>父进程关闭读取的 fd[0]，只保留写入的 fd[1]；</li>
<li>子进程关闭写入的 fd[1]，只保留读取的 fd[0]；</li>
</ul>
<h3 id="3、例子"><a href="#3、例子" class="headerlink" title="3、例子"></a>3、例子</h3><p>单个进程中的管道几乎没有任何用处。所以，通常调用 pipe 的进程接着调用 fork，这样就创建了父进程与子进程之间的 IPC 通道。如下图所示：</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200825-094212697.png" alt="mark"></p>
<p>若要数据流从父进程流向子进程，则关闭父进程的读端（<code>fd[0]</code>）与子进程的写端（<code>fd[1]</code>）；反之，则可以使数据流从子进程流向父进程。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> fd[<span class="number">2</span>];  <span class="comment">// 两个文件描述符</span></span><br><span class="line">    <span class="keyword">pid_t</span> pid;</span><br><span class="line">    <span class="keyword">char</span> buff[<span class="number">20</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(pipe(fd) &lt; <span class="number">0</span>)  <span class="comment">// 创建管道</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Create Pipe Error!\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>((pid = fork()) &lt; <span class="number">0</span>)  <span class="comment">// 创建子进程</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Fork Error!\n"</span>);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(pid &gt; <span class="number">0</span>)  <span class="comment">// 父进程</span></span><br><span class="line">    &#123;</span><br><span class="line">        close(fd[<span class="number">0</span>]); <span class="comment">// 关闭读端</span></span><br><span class="line">        write(fd[<span class="number">1</span>], <span class="string">"hello world\n"</span>, <span class="number">12</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        close(fd[<span class="number">1</span>]); <span class="comment">// 关闭写端</span></span><br><span class="line">        read(fd[<span class="number">0</span>], buff, <span class="number">20</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"%s"</span>, buff);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="二、FIFO"><a href="#二、FIFO" class="headerlink" title="二、FIFO"></a>二、FIFO</h2><p>管道还有另外一个类型是<strong>命名管道</strong>，也被叫做 <code>FIFO</code>，因为数据是先进先出的传输方式。</p>
<p>在使用命名管道前，先需要通过 <code>mkfifo</code> 命令来创建，并且指定管道名字：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ mkfifo myPipe</span><br></pre></td></tr></table></figure>

<ul>
<li>myPipe 就是这个管道的名称，基于 Linux 一切皆文件的理念，所以管道也是以文件的方式存在，我们可以用 ls 看一下，这个文件的类型是 p，也就是 pipe（管道） 的意思：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ls -l</span><br><span class="line">prw-r--r--. 1 root    root         0 Jul 17 02:45 myPipe</span><br></pre></td></tr></table></figure>

<ul>
<li>接下来，我们往 myPipe 这个管道写入数据：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ echo &quot;hello&quot; &gt; myPipe  &#x2F;&#x2F; 将数据写进管道</span><br><span class="line">                         &#x2F;&#x2F; 停住了 ...</span><br></pre></td></tr></table></figure>

<ul>
<li><p>你操作了后，你会发现命令执行后就停在这了，这是因为管道里的内容没有被读取，只有当管道里的数据被读完后，命令才可以正常退出。</p>
</li>
<li><p>于是，我们执行另外一个命令来读取这个管道里的数据：</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ cat &lt; myPipe  &#x2F;&#x2F; 读取管道里的数据</span><br><span class="line">hello</span><br></pre></td></tr></table></figure>

<ul>
<li><p>可以看到，管道里的内容被读取出来了，并打印在了终端上，另外一方面，echo 那个命令也正常退出了。</p>
</li>
<li><p>我们可以看出，<strong>管道这种通信方式效率低，不适合进程间频繁地交换数据</strong>。当然，它的好处，自然就是简单，同时也我们很容易得知管道里的数据已经被另一个进程读取了。</p>
</li>
</ul>
<h3 id="1、特点-1"><a href="#1、特点-1" class="headerlink" title="1、特点"></a>1、特点</h3><ol>
<li><strong>FIFO可以在无关的进程之间交换数据，与无名管道不同。</strong></li>
<li>FIFO有路径名与之相关联，它以一种特殊设备文件形式存在于文件系统中。</li>
</ol>
<h3 id="2、原型-1"><a href="#2、原型-1" class="headerlink" title="2、原型"></a>2、原型</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">1 #include &lt;sys&#x2F;stat.h&gt;</span><br><span class="line">2 &#x2F;&#x2F; 返回值：成功返回0，出错返回-1</span><br><span class="line">3 int mkfifo(const char *pathname, mode_t mode);</span><br></pre></td></tr></table></figure>

<p>其中的 mode 参数与<code>open</code>函数中的 mode 相同。一旦创建了一个 FIFO，就可以用一般的文件I/O函数操作它。</p>
<p>当 open 一个FIFO时，是否设置非阻塞标志（<code>O_NONBLOCK</code>）的区别：</p>
<ul>
<li>若没有指定<code>O_NONBLOCK</code>（默认），只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的，只写 open 要阻塞到某个其他进程为读而打开它。</li>
<li>若指定了<code>O_NONBLOCK</code>，则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO，其errno置ENXIO。</li>
</ul>
<h3 id="3、例子-1"><a href="#3、例子-1" class="headerlink" title="3、例子"></a>3、例子</h3><ul>
<li><strong>FIFO的通信方式类似于在进程中使用文件来传输数据</strong>，只不过FIFO类型文件同时具有管道的特性。在数据读出时，FIFO管道中同时清除数据，并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程：</li>
</ul>
<p><strong>write_fifo.c</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;   // exit</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;fcntl.h&gt;    // O_WRONLY</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;time.h&gt;     // time</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> fd;</span><br><span class="line">    <span class="keyword">int</span> n, i;</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">1024</span>];</span><br><span class="line">    <span class="keyword">time_t</span> tp;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"I am %d process.\n"</span>, getpid()); <span class="comment">// 说明进程ID</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>((fd = open(<span class="string">"fifo1"</span>, O_WRONLY)) &lt; <span class="number">0</span>) <span class="comment">// 以写打开一个FIFO</span></span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Open FIFO Failed"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">0</span>; i&lt;<span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        time(&amp;tp);  <span class="comment">// 取系统当前时间</span></span><br><span class="line">        n=<span class="built_in">sprintf</span>(buf,<span class="string">"Process %d's time is %s"</span>,getpid(),ctime(&amp;tp));</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Send message: %s"</span>, buf); <span class="comment">// 打印</span></span><br><span class="line">        <span class="keyword">if</span>(write(fd, buf, n+<span class="number">1</span>) &lt; <span class="number">0</span>)  <span class="comment">// 写入到FIFO中</span></span><br><span class="line">        &#123;</span><br><span class="line">            perror(<span class="string">"Write FIFO Failed"</span>);</span><br><span class="line">            close(fd);</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        sleep(<span class="number">1</span>);  <span class="comment">// 休眠1秒</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    close(fd);  <span class="comment">// 关闭FIFO文件</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>read_fifo.c</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;errno.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> fd;</span><br><span class="line">    <span class="keyword">int</span> len;</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">1024</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(mkfifo(<span class="string">"fifo1"</span>, <span class="number">0666</span>) &lt; <span class="number">0</span> &amp;&amp; errno!=EEXIST) <span class="comment">// 创建FIFO管道</span></span><br><span class="line">        perror(<span class="string">"Create FIFO Failed"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>((fd = open(<span class="string">"fifo1"</span>, O_RDONLY)) &lt; <span class="number">0</span>)  <span class="comment">// 以读打开FIFO</span></span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Open FIFO Failed"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>((len = read(fd, buf, <span class="number">1024</span>)) &gt; <span class="number">0</span>) <span class="comment">// 读取FIFO管道</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Read message: %s"</span>, buf);</span><br><span class="line"></span><br><span class="line">    close(fd);  <span class="comment">// 关闭FIFO文件</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在两个终端里用 gcc 分别编译运行上面两个文件，可以看到输出结果如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">2</span> I am <span class="number">5954</span> process.</span><br><span class="line"> <span class="number">3</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">28</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">4</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">29</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">5</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">30</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">6</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">31</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">7</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">32</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">8</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">33</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">9</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">34</span> <span class="number">2015</span></span><br><span class="line"><span class="number">10</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">35</span> <span class="number">2015</span></span><br><span class="line"><span class="number">11</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">36</span> <span class="number">2015</span></span><br><span class="line"><span class="number">12</span> Send message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">37</span> <span class="number">2015</span></span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"> <span class="number">2</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">28</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">3</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">29</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">4</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">30</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">5</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">31</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">6</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">32</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">7</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">33</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">8</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">34</span> <span class="number">2015</span></span><br><span class="line"> <span class="number">9</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">35</span> <span class="number">2015</span></span><br><span class="line"><span class="number">10</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">36</span> <span class="number">2015</span></span><br><span class="line"><span class="number">11</span> Read message: Process <span class="number">5954'</span>s time is Mon Apr <span class="number">20</span> <span class="number">12</span>:<span class="number">37</span>:<span class="number">37</span> <span class="number">2015</span></span><br></pre></td></tr></table></figure>

<p>上述例子可以扩展成 客户进程—服务器进程 通信的实例，<code>write_fifo</code>的作用类似于客户端，可以打开多个客户端向一个服务器发送请求信息，<code>read_fifo</code>类似于服务器，它适时监控着FIFO的读端，当有数据时，读出并进行处理，但是有一个关键的问题是，每一个客户端必须预先知道服务器提供的FIFO接口，下图显示了这种安排：</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200825-100921142.png" alt="mark"></p>
<h2 id="三、消息队列"><a href="#三、消息队列" class="headerlink" title="三、消息队列"></a>三、消息队列</h2><p>前面说到管道的通信方式是效率低的，因此管道不适合进程间频繁地交换数据。</p>
<p>对于这个问题，<strong>消息队列</strong>的通信模式就可以解决。比如，A 进程要给 B 进程发送消息，A 进程把数据放在对应的消息队列后就可以正常返回了，B 进程需要的时候再去读取数据就可以了。同理，B 进程要给 A 进程发送消息也是如此。</p>
<h3 id="1、特点-2"><a href="#1、特点-2" class="headerlink" title="1、特点"></a>1、特点</h3><ol>
<li>消息队列是面向记录的，其中的消息具有特定的格式以及特定的优先级。</li>
<li><strong>消息队列独立于发送与接收进程。进程终止时，消息队列及其内容并不会被删除。</strong></li>
<li><strong>消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。</strong></li>
</ol>
<h3 id="2、原型-2"><a href="#2、原型-2" class="headerlink" title="2、原型"></a>2、原型</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/msg.h&gt;</span></span></span><br><span class="line"><span class="comment">// 创建或打开消息队列：成功返回队列ID，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">msgget</span><span class="params">(<span class="keyword">key_t</span> key, <span class="keyword">int</span> flag)</span></span>;</span><br><span class="line"><span class="comment">// 添加消息：成功返回0，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">msgsnd</span><span class="params">(<span class="keyword">int</span> msqid, <span class="keyword">const</span> <span class="keyword">void</span> *ptr, <span class="keyword">size_t</span> size, <span class="keyword">int</span> flag)</span></span>;</span><br><span class="line"><span class="comment">// 读取消息：成功返回消息数据的长度，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">msgrcv</span><span class="params">(<span class="keyword">int</span> msqid, <span class="keyword">void</span> *ptr, <span class="keyword">size_t</span> size, <span class="keyword">long</span> type,<span class="keyword">int</span> flag)</span></span>;</span><br><span class="line"><span class="comment">// 控制消息队列：成功返回0，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">msgctl</span><span class="params">(<span class="keyword">int</span> msqid, <span class="keyword">int</span> cmd, struct msqid_ds *buf)</span></span>;</span><br></pre></td></tr></table></figure>

<p>在以下两种情况下，<code>msgget</code>将创建一个新的消息队列：</p>
<ul>
<li>如果没有与键值key相对应的消息队列，并且flag中包含了<code>IPC_CREAT</code>标志位。</li>
<li>key参数为<code>IPC_PRIVATE</code>。</li>
</ul>
<p>函数<code>msgrcv</code>在读取消息队列时，type参数有下面几种情况：</p>
<ul>
<li><code>type == 0</code>，返回队列中的第一个消息；</li>
<li><code>type &gt; 0</code>，返回队列中消息类型为 type 的第一个消息；</li>
<li><code>type &lt; 0</code>，返回队列中消息类型值小于或等于 type 绝对值的消息，如果有多个，则取类型值最小的消息。</li>
</ul>
<p>可以看出，type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。（其他的参数解释，请自行Google之）</p>
<h3 id="3、例子-2"><a href="#3、例子-2" class="headerlink" title="3、例子"></a>3、例子</h3><p>下面写了一个简单的使用消息队列进行IPC的例子，服务端程序一直在等待特定类型的消息，当收到该类型的消息以后，发送另一种特定类型的消息作为反馈，客户端读取该反馈并打印出来。</p>
<p><strong>msg_server.c</strong></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/msg.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于创建一个唯一的key</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MSG_FILE <span class="meta-string">"/etc/passwd"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 消息结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> &#123;</span></span><br><span class="line">    <span class="keyword">long</span> mtype;</span><br><span class="line">    <span class="keyword">char</span> mtext[<span class="number">256</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> msqid;</span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> <span class="title">msg</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取key值</span></span><br><span class="line">    <span class="keyword">if</span>((key = ftok(MSG_FILE,<span class="string">'z'</span>)) &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"ftok error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打印key值</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Message Queue - Server key is: %d.\n"</span>, key);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建消息队列</span></span><br><span class="line">    <span class="keyword">if</span> ((msqid = msgget(key, IPC_CREAT|<span class="number">0777</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"msgget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打印消息队列ID及进程ID</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"My msqid is: %d.\n"</span>, msqid);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"My pid is: %d.\n"</span>, getpid());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 循环读取消息</span></span><br><span class="line">    <span class="keyword">for</span>(;;)</span><br><span class="line">    &#123;</span><br><span class="line">        msgrcv(msqid, &amp;msg, <span class="number">256</span>, <span class="number">888</span>, <span class="number">0</span>);<span class="comment">// 返回类型为888的第一个消息</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Server: receive msg.mtext is: %s.\n"</span>, msg.mtext);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Server: receive msg.mtype is: %d.\n"</span>, msg.mtype);</span><br><span class="line"></span><br><span class="line">        msg.mtype = <span class="number">999</span>; <span class="comment">// 客户端接收的消息类型</span></span><br><span class="line">        <span class="built_in">sprintf</span>(msg.mtext, <span class="string">"hello, I'm server %d"</span>, getpid());</span><br><span class="line">        msgsnd(msqid, &amp;msg, <span class="keyword">sizeof</span>(msg.mtext), <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>msg_client.c</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/msg.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于创建一个唯一的key</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MSG_FILE <span class="meta-string">"/etc/passwd"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 消息结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> &#123;</span></span><br><span class="line">    <span class="keyword">long</span> mtype;</span><br><span class="line">    <span class="keyword">char</span> mtext[<span class="number">256</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> msqid;</span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> <span class="title">msg</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取key值</span></span><br><span class="line">    <span class="keyword">if</span> ((key = ftok(MSG_FILE, <span class="string">'z'</span>)) &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"ftok error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打印key值</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Message Queue - Client key is: %d.\n"</span>, key);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打开消息队列</span></span><br><span class="line">    <span class="keyword">if</span> ((msqid = msgget(key, IPC_CREAT|<span class="number">0777</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"msgget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打印消息队列ID及进程ID</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"My msqid is: %d.\n"</span>, msqid);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"My pid is: %d.\n"</span>, getpid());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 添加消息，类型为888</span></span><br><span class="line">    msg.mtype = <span class="number">888</span>;</span><br><span class="line">    <span class="built_in">sprintf</span>(msg.mtext, <span class="string">"hello, I'm client %d"</span>, getpid());</span><br><span class="line">    msgsnd(msqid, &amp;msg, <span class="keyword">sizeof</span>(msg.mtext), <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取类型为777的消息</span></span><br><span class="line">    msgrcv(msqid, &amp;msg, <span class="number">256</span>, <span class="number">999</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Client: receive msg.mtext is: %s.\n"</span>, msg.mtext);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"Client: receive msg.mtype is: %d.\n"</span>, msg.mtype);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li></li>
</ul>
<h3 id="3、例子-3"><a href="#3、例子-3" class="headerlink" title="3、例子"></a>3、例子</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/sem.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 联合体，用于semctl初始化</span></span><br><span class="line"><span class="keyword">union</span> semun</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">int</span>              val; <span class="comment">/*for SETVAL*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">semid_ds</span> *<span class="title">buf</span>;</span></span><br><span class="line">    <span class="keyword">unsigned</span> short  *<span class="built_in">array</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化信号量</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">init_sem</span><span class="params">(<span class="keyword">int</span> sem_id, <span class="keyword">int</span> value)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">union</span> semun tmp;</span><br><span class="line">    tmp.val = value;</span><br><span class="line">    <span class="keyword">if</span>(semctl(sem_id, <span class="number">0</span>, SETVAL, tmp) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Init Semaphore Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// P操作:</span></span><br><span class="line"><span class="comment">//    若信号量值为1，获取资源并将信号量值-1</span></span><br><span class="line"><span class="comment">//    若信号量值为0，进程挂起等待</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sem_p</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sbuf</span>;</span></span><br><span class="line">    sbuf.sem_num = <span class="number">0</span>; <span class="comment">/*序号*/</span></span><br><span class="line">    sbuf.sem_op = <span class="number">-1</span>; <span class="comment">/*P操作*/</span></span><br><span class="line">    sbuf.sem_flg = SEM_UNDO;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(semop(sem_id, &amp;sbuf, <span class="number">1</span>) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"P operation Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// V操作：</span></span><br><span class="line"><span class="comment">//    释放资源并将信号量值+1</span></span><br><span class="line"><span class="comment">//    如果有进程正在挂起等待，则唤醒它们</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sem_v</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sbuf</span>;</span></span><br><span class="line">    sbuf.sem_num = <span class="number">0</span>; <span class="comment">/*序号*/</span></span><br><span class="line">    sbuf.sem_op = <span class="number">1</span>;  <span class="comment">/*V操作*/</span></span><br><span class="line">    sbuf.sem_flg = SEM_UNDO;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(semop(sem_id, &amp;sbuf, <span class="number">1</span>) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"V operation Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除信号量集</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">del_sem</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">union</span> semun tmp;</span><br><span class="line">    <span class="keyword">if</span>(semctl(sem_id, <span class="number">0</span>, IPC_RMID, tmp) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Delete Semaphore Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> sem_id;  <span class="comment">// 信号量集ID</span></span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="keyword">pid_t</span> pid;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取key值</span></span><br><span class="line">    <span class="keyword">if</span>((key = ftok(<span class="string">"."</span>, <span class="string">'z'</span>)) &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"ftok error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建信号量集，其中只有一个信号量</span></span><br><span class="line">    <span class="keyword">if</span>((sem_id = semget(key, <span class="number">1</span>, IPC_CREAT|<span class="number">0666</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化：初值设为0资源被占用</span></span><br><span class="line">    init_sem(sem_id, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>((pid = fork()) == <span class="number">-1</span>)</span><br><span class="line">        perror(<span class="string">"Fork Error"</span>);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(pid == <span class="number">0</span>) <span class="comment">/*子进程*/</span></span><br><span class="line">    &#123;</span><br><span class="line">        sleep(<span class="number">2</span>);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Process child: pid=%d\n"</span>, getpid());</span><br><span class="line">        sem_v(sem_id);  <span class="comment">/*释放资源*/</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span>  <span class="comment">/*父进程*/</span></span><br><span class="line">    &#123;</span><br><span class="line">        sem_p(sem_id);   <span class="comment">/*等待资源*/</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Process father: pid=%d\n"</span>, getpid());</span><br><span class="line">        sem_v(sem_id);   <span class="comment">/*释放资源*/</span></span><br><span class="line">        del_sem(sem_id); <span class="comment">/*删除信号量集*/</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的例子如果不加信号量，则父进程会先执行完毕。这里加了信号量让父进程等待子进程执行完以后再执行。</p>
<h2 id="四、共享内存"><a href="#四、共享内存" class="headerlink" title="四、共享内存"></a>四、共享内存</h2><ul>
<li>消息队列的读取和写入的过程，都会有发生用户态与内核态之间的消息拷贝过程。那<strong>共享内存</strong>的方式，就很好的解决了这一问题。</li>
<li>现代操作系统，对于内存管理，采用的是虚拟内存技术，也就是每个进程都有自己独立的虚拟内存空间，不同进程的虚拟内存映射到不同的物理内存中。所以，即使进程 A 和 进程 B 的虚拟地址是一样的，其实访问的是不同的物理内存地址，对于数据的增删查改互不影响。</li>
<li><strong>共享内存的机制，就是拿出一块虚拟地址空间来，映射到相同的物理内存中</strong>。这样这个进程写入的东西，另外一个进程马上就能看到了，都不需要拷贝来拷贝去，传来传去，大大提高了进程间通信的速度。</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200927/104938057.png" alt="mark"></p>
<h3 id="1、特点-3"><a href="#1、特点-3" class="headerlink" title="1、特点"></a>1、特点</h3><ol>
<li>共享内存是最快的一种 IPC，因为进程是直接对内存进行存取。</li>
<li>因为多个进程可以同时操作，所以需要进行同步。</li>
<li><strong>信号量+共享内存通常结合在一起使用，信号量用来同步对共享内存的访问。</strong></li>
</ol>
<h3 id="2、原型-3"><a href="#2、原型-3" class="headerlink" title="2、原型"></a>2、原型</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/shm.h&gt;</span></span></span><br><span class="line"><span class="comment">// 创建或获取一个共享内存：成功返回共享内存ID，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">shmget</span><span class="params">(<span class="keyword">key_t</span> key, <span class="keyword">size_t</span> size, <span class="keyword">int</span> flag)</span></span>;</span><br><span class="line"><span class="comment">// 连接共享内存到当前进程的地址空间：成功返回指向共享内存的指针，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">shmat</span><span class="params">(<span class="keyword">int</span> shm_id, <span class="keyword">const</span> <span class="keyword">void</span> *addr, <span class="keyword">int</span> flag)</span></span>;</span><br><span class="line"><span class="comment">// 断开与共享内存的连接：成功返回0，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">shmdt</span><span class="params">(<span class="keyword">void</span> *addr)</span></span>;</span><br><span class="line"><span class="comment">// 控制共享内存的相关信息：成功返回0，失败返回-1</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">shmctl</span><span class="params">(<span class="keyword">int</span> shm_id, <span class="keyword">int</span> cmd, struct shmid_ds *buf)</span></span>;</span><br></pre></td></tr></table></figure>

<p>当用<code>shmget</code>函数创建一段共享内存时，必须指定其 size；而如果引用一个已存在的共享内存，则将 size 指定为0 。</p>
<p>当一段共享内存被创建以后，它并不能被任何进程访问。必须使用<code>shmat</code>函数连接该共享内存到当前进程的地址空间，连接成功后把共享内存区对象映射到调用进程的地址空间，随后可像本地空间一样访问。</p>
<p><code>shmdt</code>函数是用来断开<code>shmat</code>建立的连接的。注意，这并不是从系统中删除该共享内存，只是当前进程不能再访问该共享内存而已。</p>
<p><code>shmctl</code>函数可以对共享内存执行多种操作，根据参数 cmd 执行相应的操作。常用的是<code>IPC_RMID</code>（从系统中删除该共享内存）。</p>
<h3 id="3、例子-4"><a href="#3、例子-4" class="headerlink" title="3、例子"></a>3、例子</h3><p>下面这个例子，使用了【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信。</p>
<ul>
<li>共享内存用来传递数据；</li>
<li>信号量用来同步；</li>
<li>消息队列用来 在客户端修改了共享内存后 通知服务器读取。</li>
</ul>
<p><strong>server.c</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/shm.h&gt;  // shared memory</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/sem.h&gt;  // semaphore</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/msg.h&gt;  // message queue</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;   // memcpy</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 消息队列结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> &#123;</span></span><br><span class="line">    <span class="keyword">long</span> mtype;</span><br><span class="line">    <span class="keyword">char</span> mtext;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 联合体，用于semctl初始化</span></span><br><span class="line"><span class="keyword">union</span> semun</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">int</span>              val; <span class="comment">/*for SETVAL*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">semid_ds</span> *<span class="title">buf</span>;</span></span><br><span class="line">    <span class="keyword">unsigned</span> short  *<span class="built_in">array</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化信号量</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">init_sem</span><span class="params">(<span class="keyword">int</span> sem_id, <span class="keyword">int</span> value)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">union</span> semun tmp;</span><br><span class="line">    tmp.val = value;</span><br><span class="line">    <span class="keyword">if</span>(semctl(sem_id, <span class="number">0</span>, SETVAL, tmp) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Init Semaphore Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// P操作:</span></span><br><span class="line"><span class="comment">//  若信号量值为1，获取资源并将信号量值-1</span></span><br><span class="line"><span class="comment">//  若信号量值为0，进程挂起等待</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sem_p</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sbuf</span>;</span></span><br><span class="line">    sbuf.sem_num = <span class="number">0</span>; <span class="comment">/*序号*/</span></span><br><span class="line">    sbuf.sem_op = <span class="number">-1</span>; <span class="comment">/*P操作*/</span></span><br><span class="line">    sbuf.sem_flg = SEM_UNDO;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(semop(sem_id, &amp;sbuf, <span class="number">1</span>) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"P operation Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// V操作：</span></span><br><span class="line"><span class="comment">//  释放资源并将信号量值+1</span></span><br><span class="line"><span class="comment">//  如果有进程正在挂起等待，则唤醒它们</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sem_v</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sbuf</span>;</span></span><br><span class="line">    sbuf.sem_num = <span class="number">0</span>; <span class="comment">/*序号*/</span></span><br><span class="line">    sbuf.sem_op = <span class="number">1</span>;  <span class="comment">/*V操作*/</span></span><br><span class="line">    sbuf.sem_flg = SEM_UNDO;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(semop(sem_id, &amp;sbuf, <span class="number">1</span>) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"V operation Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除信号量集</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">del_sem</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">union</span> semun tmp;</span><br><span class="line">    <span class="keyword">if</span>(semctl(sem_id, <span class="number">0</span>, IPC_RMID, tmp) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Delete Semaphore Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建一个信号量集</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">creat_sem</span><span class="params">(<span class="keyword">key_t</span> key)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> sem_id;</span><br><span class="line">    <span class="keyword">if</span>((sem_id = semget(key, <span class="number">1</span>, IPC_CREAT|<span class="number">0666</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    init_sem(sem_id, <span class="number">1</span>);  <span class="comment">/*初值设为1资源未占用*/</span></span><br><span class="line">    <span class="keyword">return</span> sem_id;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="keyword">int</span> shmid, semid, msqid;</span><br><span class="line">    <span class="keyword">char</span> *shm;</span><br><span class="line">    <span class="keyword">char</span> data[] = <span class="string">"this is server"</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">shmid_ds</span> <span class="title">buf1</span>;</span>  <span class="comment">/*用于删除共享内存*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">msqid_ds</span> <span class="title">buf2</span>;</span>  <span class="comment">/*用于删除消息队列*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> <span class="title">msg</span>;</span>  <span class="comment">/*消息队列用于通知对方更新了共享内存*/</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取key值</span></span><br><span class="line">    <span class="keyword">if</span>((key = ftok(<span class="string">"."</span>, <span class="string">'z'</span>)) &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"ftok error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建共享内存</span></span><br><span class="line">    <span class="keyword">if</span>((shmid = shmget(key, <span class="number">1024</span>, IPC_CREAT|<span class="number">0666</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Create Shared Memory Error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 连接共享内存</span></span><br><span class="line">    shm = (<span class="keyword">char</span>*)shmat(shmid, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span>((<span class="keyword">int</span>)shm == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Attach Shared Memory Error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建消息队列</span></span><br><span class="line">    <span class="keyword">if</span> ((msqid = msgget(key, IPC_CREAT|<span class="number">0777</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"msgget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建信号量</span></span><br><span class="line">    semid = creat_sem(key);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读数据</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        msgrcv(msqid, &amp;msg, <span class="number">1</span>, <span class="number">888</span>, <span class="number">0</span>); <span class="comment">/*读取类型为888的消息*/</span></span><br><span class="line">        <span class="keyword">if</span>(msg.mtext == <span class="string">'q'</span>)  <span class="comment">/*quit - 跳出循环*/</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">if</span>(msg.mtext == <span class="string">'r'</span>)  <span class="comment">/*read - 读共享内存*/</span></span><br><span class="line">        &#123;</span><br><span class="line">            sem_p(semid);</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">"%s\n"</span>,shm);</span><br><span class="line">            sem_v(semid);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 断开连接</span></span><br><span class="line">    shmdt(shm);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*删除共享内存、消息队列、信号量*/</span></span><br><span class="line">    shmctl(shmid, IPC_RMID, &amp;buf1);</span><br><span class="line">    msgctl(msqid, IPC_RMID, &amp;buf2);</span><br><span class="line">    del_sem(semid);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>client.c</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/shm.h&gt;  // shared memory</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/sem.h&gt;  // semaphore</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;sys/msg.h&gt;  // message queue</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string">&lt;string.h&gt;   // memcpy</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 消息队列结构</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> &#123;</span></span><br><span class="line">    <span class="keyword">long</span> mtype;</span><br><span class="line">    <span class="keyword">char</span> mtext;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 联合体，用于semctl初始化</span></span><br><span class="line"><span class="keyword">union</span> semun</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">int</span>              val; <span class="comment">/*for SETVAL*/</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">semid_ds</span> *<span class="title">buf</span>;</span></span><br><span class="line">    <span class="keyword">unsigned</span> short  *<span class="built_in">array</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// P操作:</span></span><br><span class="line"><span class="comment">//  若信号量值为1，获取资源并将信号量值-1</span></span><br><span class="line"><span class="comment">//  若信号量值为0，进程挂起等待</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sem_p</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sbuf</span>;</span></span><br><span class="line">    sbuf.sem_num = <span class="number">0</span>; <span class="comment">/*序号*/</span></span><br><span class="line">    sbuf.sem_op = <span class="number">-1</span>; <span class="comment">/*P操作*/</span></span><br><span class="line">    sbuf.sem_flg = SEM_UNDO;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(semop(sem_id, &amp;sbuf, <span class="number">1</span>) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"P operation Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// V操作：</span></span><br><span class="line"><span class="comment">//  释放资源并将信号量值+1</span></span><br><span class="line"><span class="comment">//  如果有进程正在挂起等待，则唤醒它们</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sem_v</span><span class="params">(<span class="keyword">int</span> sem_id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sembuf</span> <span class="title">sbuf</span>;</span></span><br><span class="line">    sbuf.sem_num = <span class="number">0</span>; <span class="comment">/*序号*/</span></span><br><span class="line">    sbuf.sem_op = <span class="number">1</span>;  <span class="comment">/*V操作*/</span></span><br><span class="line">    sbuf.sem_flg = SEM_UNDO;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(semop(sem_id, &amp;sbuf, <span class="number">1</span>) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"V operation Error"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">key_t</span> key;</span><br><span class="line">    <span class="keyword">int</span> shmid, semid, msqid;</span><br><span class="line">    <span class="keyword">char</span> *shm;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">msg_form</span> <span class="title">msg</span>;</span></span><br><span class="line">    <span class="keyword">int</span> flag = <span class="number">1</span>; <span class="comment">/*while循环条件*/</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取key值</span></span><br><span class="line">    <span class="keyword">if</span>((key = ftok(<span class="string">"."</span>, <span class="string">'z'</span>)) &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"ftok error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取共享内存</span></span><br><span class="line">    <span class="keyword">if</span>((shmid = shmget(key, <span class="number">1024</span>, <span class="number">0</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"shmget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 连接共享内存</span></span><br><span class="line">    shm = (<span class="keyword">char</span>*)shmat(shmid, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span>((<span class="keyword">int</span>)shm == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"Attach Shared Memory Error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建消息队列</span></span><br><span class="line">    <span class="keyword">if</span> ((msqid = msgget(key, <span class="number">0</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"msgget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取信号量</span></span><br><span class="line">    <span class="keyword">if</span>((semid = semget(key, <span class="number">0</span>, <span class="number">0</span>)) == <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        perror(<span class="string">"semget error"</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 写数据</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"***************************************\n"</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"*                 IPC                 *\n"</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"*    Input r to send data to server.  *\n"</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"*    Input q to quit.                 *\n"</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"***************************************\n"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(flag)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">char</span> c;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"Please input command: "</span>);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">"%c"</span>, &amp;c);</span><br><span class="line">        <span class="keyword">switch</span>(c)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">'r'</span>:</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">"Data to send: "</span>);</span><br><span class="line">                sem_p(semid);  <span class="comment">/*访问资源*/</span></span><br><span class="line">                <span class="built_in">scanf</span>(<span class="string">"%s"</span>, shm);</span><br><span class="line">                sem_v(semid);  <span class="comment">/*释放资源*/</span></span><br><span class="line">                <span class="comment">/*清空标准输入缓冲区*/</span></span><br><span class="line">                <span class="keyword">while</span>((c=getchar())!=<span class="string">'\n'</span> &amp;&amp; c!=EOF);</span><br><span class="line">                msg.mtype = <span class="number">888</span>;</span><br><span class="line">                msg.mtext = <span class="string">'r'</span>;  <span class="comment">/*发送消息通知服务器读数据*/</span></span><br><span class="line">                msgsnd(msqid, &amp;msg, <span class="keyword">sizeof</span>(msg.mtext), <span class="number">0</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">'q'</span>:</span><br><span class="line">                msg.mtype = <span class="number">888</span>;</span><br><span class="line">                msg.mtext = <span class="string">'q'</span>;</span><br><span class="line">                msgsnd(msqid, &amp;msg, <span class="keyword">sizeof</span>(msg.mtext), <span class="number">0</span>);</span><br><span class="line">                flag = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="built_in">printf</span>(<span class="string">"Wrong input!\n"</span>);</span><br><span class="line">                <span class="comment">/*清空标准输入缓冲区*/</span></span><br><span class="line">                <span class="keyword">while</span>((c=getchar())!=<span class="string">'\n'</span> &amp;&amp; c!=EOF);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 断开连接</span></span><br><span class="line">    shmdt(shm);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意：当<code>scanf()</code>输入字符或字符串时，缓冲区中遗留下了<code>\n</code>，所以每次输入操作后都需要清空标准输入的缓冲区。但是由于 gcc 编译器不支持<code>fflush(stdin)</code>（它只是标准C的扩展），所以我们使用了替代方案：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1 while((c&#x3D;getchar())!&#x3D;&#39;\n&#39; &amp;&amp; c!&#x3D;EOF);</span><br></pre></td></tr></table></figure>



<h2 id="五、信号量"><a href="#五、信号量" class="headerlink" title="五、信号量"></a>五、信号量</h2><h3 id="1-特点"><a href="#1-特点" class="headerlink" title="1. 特点"></a>1. 特点</h3><ul>
<li>用了共享内存通信方式，带来新的问题，那就是如果多个进程同时修改同一个共享内存，很有可能就冲突了。例如两个进程都同时写一个地址，那先写的那个进程会发现内容被别人覆盖了。</li>
<li>为了防止多进程竞争共享资源，而造成的数据错乱，所以需要保护机制，使得共享的资源，在任意时刻只能被一个进程访问。正好，<strong>信号量</strong>就实现了这一保护机制。</li>
</ul>
<p><strong>信号量其实是一个整型的计数器，主要用于实现进程间的互斥与同步，而不是用于缓存进程间通信的数据</strong>。</p>
<h3 id="2-PV操作"><a href="#2-PV操作" class="headerlink" title="2. PV操作"></a>2. PV操作</h3><p>信号量表示资源的数量，控制信号量的方式有两种原子操作：</p>
<ul>
<li><p>一个是 <strong>P 操作</strong>，这个操作会把信号量减去 -1，相减后如果信号量 &lt; 0，则表明资源已被占用，进程需阻塞等待；相减后如果信号量 &gt;= 0，则表明还有资源可使用，进程可正常继续执行。</p>
</li>
<li><p>另一个是 <strong>V 操作</strong>，这个操作会把信号量加上 1，相加后如果信号量 &lt;= 0，则表明当前有阻塞中的进程，于是会将该进程唤醒运行；相加后如果信号量 &gt; 0，则表明当前没有阻塞中的进程；</p>
</li>
<li><p>P 操作是用在进入共享资源之前，V 操作是用在离开共享资源之后，这两个操作是必须成对出现的。</p>
</li>
</ul>
<h3 id="3-例子"><a href="#3-例子" class="headerlink" title="3. 例子"></a>3. 例子</h3><p>接下来，举个例子，如果要使得两个<strong>进程互斥访问共享内存</strong>，我们可<strong>以初始化信号量为 <code>1</code></strong>。</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200927/105626287.png" alt="mark"></p>
<p>具体的过程如下：</p>
<ul>
<li><p>进程 A 在访问共享内存前，先执行了 P 操作，由于信号量的初始值为 1，故在进程 A 执行 P 操作后信号量变为 0，表示共享资源可用，于是进程 A 就可以访问共享内存。</p>
</li>
<li><p>若此时，进程 B 也想访问共享内存，执行了 P 操作，结果信号量变为了 -1，这就意味着临界资源已被占用，因此进程 B 被阻塞。</p>
</li>
<li><p>直到进程 A 访问完共享内存，才会执行 V 操作，使得信号量恢复为 0，接着就会唤醒阻塞中的线程 B，使得进程 B 可以访问共享内存，最后完成共享内存的访问后，执行 V 操作，使信号量恢复到初始值 1。</p>
</li>
<li><p>可以发现，信号初始化为 <code>1</code>，就代表着是<strong>互斥信号量</strong>，它可以保证共享内存在任何时刻只有一个进程在访问，这就很好的保护了共享内存。</p>
</li>
</ul>
<p>另外，在多进程里，每个进程并不一定是顺序执行的，它们基本是以各自独立的、不可预知的速度向前推进，但有时候我们又希望多个进程能密切合作，以实现一个共同的任务。</p>
<ul>
<li>例如，进程 A 是负责生产数据，而进程 B 是负责读取数据，这两个进程是相互合作、相互依赖的，进程 A 必须先生产了数据，进程 B 才能读取到数据，所以执行是有前后顺序的。</li>
<li>那么这时候，就可以用信号量来实现多进程同步的方式，我们可以初始化信号量为<code>0</code>。</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200927/105738863.png" alt="mark"></p>
<p>具体过程：</p>
<ul>
<li>如果进程 B 比进程 A 先执行了，那么执行到 P 操作时，由于信号量初始值为 0，故信号量会变为 -1，表示进程 A 还没生产数据，于是进程 B 就阻塞等待；</li>
<li>接着，当进程 A 生产完数据后，执行了 V 操作，就会使得信号量变为 0，于是就会唤醒阻塞在 P 操作的进程 B；</li>
<li>最后，进程 B 被唤醒后，意味着进程 A 已经生产了数据，于是进程 B 就可以正常读取数据了。</li>
</ul>
<p>可以发现，信号初始化为 <code>0</code>，就代表着是<strong>同步信号量</strong>，它可以保证进程 A 应在进程 B 之前执行。</p>
<h2 id="六-信号"><a href="#六-信号" class="headerlink" title="六. 信号"></a>六. 信号</h2><h3 id="1-特点-1"><a href="#1-特点-1" class="headerlink" title="1. 特点"></a>1. 特点</h3><ul>
<li><p>上面说的进程间通信，都是常规状态下的工作模式。<strong>对于异常情况下的工作模式，就需要用「信号」的方式来通知进程。</strong></p>
</li>
<li><p>信号跟信号量虽然名字相似度 66.66%，但两者用途完全不一样，就好像 Java 和 JavaScript 的区别。</p>
</li>
<li><p>在 Linux 操作系统中， 为了响应各种各样的事件，提供了几十种信号，分别代表不同的意义。我们可以通过 <code>kill -l</code> 命令，查看所有的信号：</p>
</li>
</ul>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">kill</span> -l</span><br><span class="line"> 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP</span><br><span class="line"> 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1</span><br><span class="line">11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM</span><br><span class="line">16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP</span><br><span class="line">21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ</span><br><span class="line">26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR</span><br><span class="line">31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3</span><br><span class="line">38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8</span><br><span class="line">43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13</span><br><span class="line">48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12</span><br><span class="line">53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7</span><br><span class="line">58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2</span><br><span class="line">63) SIGRTMAX-1  64) SIGRTMAX</span><br></pre></td></tr></table></figure>



<p>运行在 shell 终端的进程，我们可以通过键盘输入某些组合键的时候，给进程发送信号。例如</p>
<ul>
<li>Ctrl+C 产生 <code>SIGINT</code> 信号，表示终止该进程；</li>
<li>Ctrl+Z 产生 <code>SIGTSTP</code> 信号，表示停止该进程，但还未结束；</li>
</ul>
<p>如果进程在后台运行，可以通过 <code>kill</code> 命令的方式给进程发送信号，但前提需要知道运行中的进程 PID 号，例如：</p>
<ul>
<li>kill -9 1050 ，表示给 PID 为 1050 的进程发送 <code>SIGKILL</code> 信号，用来立即结束该进程；</li>
</ul>
<p><strong>所以，信号事件的来源主要有硬件来源（如键盘 Cltr+C ）和软件来源（如 kill 命令）。</strong></p>
<p>信号是进程间通信机制中<strong>唯一的异步通信机制</strong>，因为可以在任何时候发送信号给某一进程，一旦有信号产生，我们就有下面这几种，用户进程对信号的处理方式。</p>
<ul>
<li><strong>1.执行默认操作</strong>。Linux 对每种信号都规定了默认操作，例如，上面列表中的 SIGTERM 信号，就是终止进程的意思。Core 的意思是 Core Dump，也即终止进程后，通过 Core Dump 将当前进程的运行状态保存在文件里面，方便程序员事后进行分析问题在哪里。</li>
<li><strong>2.捕捉信号</strong>。我们可以为信号定义一个信号处理函数。当信号发生时，我们就执行相应的信号处理函数。</li>
<li><strong>3.忽略信号</strong>。当我们不希望处理某些信号的时候，就可以忽略该信号，不做任何处理。有两个信号是应用进程无法捕捉和忽略的，即 <code>SIGKILL</code> 和 <code>SEGSTOP</code>，它们用于在任何时候中断或结束某一进程。</li>
</ul>
<h2 id="五种通讯方式总结"><a href="#五种通讯方式总结" class="headerlink" title="五种通讯方式总结"></a>五种通讯方式总结</h2><p>1.管道：速度慢，容量有限，只有父子进程能通讯</p>
<p>2.FIFO：任何进程间都能通讯，但速度慢   </p>
<p>3.消息队列：容量受到系统限制，且要注意第一次读的时候，要考虑上一次没有读完数据的问题   </p>
<p>4.信号量：不能传递复杂消息，只能用来同步   </p>
<p>5.共享内存区：能够很容易控制容量，速度快，但要保持同步，比如一个进程在写的时候，另一个进程要注意读写的问题，相当于线程中的线程安全，当然，共享内存区同样可以用作线程间通讯，不过没这个必要，线程间本来就已经共享了同一进程内的一块内存</p>
<p><strong>参考博客</strong> ： <a href="https://mp.weixin.qq.com/s/mblyh6XrLj1bCwL0Evs-Vg" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/mblyh6XrLj1bCwL0Evs-Vg</a></p>

      
      <!-- reward -->
      
      <div id="reward-btn">
        打赏
      </div>
      
    </div>
      <!-- copyright -->
      
        <div class="declare">
          <ul class="post-copyright">
            <li>
              <i class="ri-copyright-line"></i>
              <strong>版权声明： </strong s>
              本博客所有文章除特别声明外，均采用 <a href="https://www.apache.org/licenses/LICENSE-2.0.html" rel="external nofollow"
                target="_blank">Apache License 2.0</a> 许可协议。转载请注明出处！
            </li>
          </ul>
        </div>
        
    <footer class="article-footer">
      
          
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://zhuuu.work/2020/08/25/Linux/Linux-02-%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F/" rel="tag">进程通信方式</a></li></ul>


    </footer>

  </div>

  
  
  <nav class="article-nav">
    
      <a href="/2020/08/25/Leetcode/Leetcode-459-%E9%87%8D%E5%A4%8D%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Leetcode-459-重复的子字符串
          
        </div>
      </a>
    
    
      <a href="/2020/08/25/Mysql/Mysql-12-%E4%B8%80%E5%AF%B9%E5%A4%9A,%E5%A4%9A%E5%AF%B9%E4%B8%80%EF%BC%8C%E5%A4%9A%E5%AF%B9%E5%A4%9A%E9%97%AE%E9%A2%98/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Mysql-12-一对多,多对一，多对多问题</div>
      </a>
    
  </nav>


  

  
  
<!-- valine评论 -->
<div id="vcomments-box">
    <div id="vcomments">
    </div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src='https://cdn.jsdelivr.net/npm/valine@1.3.10/dist/Valine.min.js'></script>
<script>
    new Valine({
        el: '#vcomments',
        notify: false,
        verify: '',
        app_id: '',
        app_key: '',
        path: window.location.pathname,
        avatar: 'mp',
        placeholder: '给我的文章加点评论吧~',
        recordIP: true
    });
    const infoEle = document.querySelector('#vcomments .info');
    if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
        infoEle.childNodes.forEach(function (item) {
            item.parentNode.removeChild(item);
        });
    }
</script>
<style>
    #vcomments-box {
        padding: 5px 30px;
    }

    @media screen and (max-width: 800px) {
        #vcomments-box {
            padding: 5px 0px;
        }
    }

    #vcomments-box #vcomments {
        background-color: #fff;
    }

    .v .vlist .vcard .vh {
        padding-right: 20px;
    }

    .v .vlist .vcard {
        padding-left: 10px;
    }
</style>

  

  
  
<div class="gitalk" id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1.5.0/dist/gitalk.css">


<script src="https://cdn.jsdelivr.net/npm/gitalk@1.5.0/dist/gitalk.min.js"></script>


<script src="https://cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script>

<script type="text/javascript">
  var gitalk = new Gitalk({
    clientID: 'db188ed8c86dc4b0dbf3',
    clientSecret: 'a58f92160e5a9efd726b7d533000a0737f3e3f3e',
    repo: 'Blog-comments',
    owner: 'Zhuuuuuuuu',
    admin: ['Zhuuuuuuuu'],
    // id: location.pathname,      // Ensure uniqueness and length less than 50
    id: md5(location.pathname),
    distractionFreeMode: false,  // Facebook-like distraction free mode
    pagerDirection: 'last'
  })

  gitalk.render('gitalk-container')
</script>

  

</article>
</section>
      <footer class="footer">
  <div class="outer">
    <ul class="list-inline">
      <li>
        &copy;
        2019-2021
        Zhuuu
      </li>
      <li>
        
      </li>
    </ul>
    <ul class="list-inline">
      <li>
        
        
        <span>
  <i>PV:<span id="busuanzi_value_page_pv"></span></i>
  <i>UV:<span id="busuanzi_value_site_uv"></span></i>
</span>
        
      </li>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
    <div class="to_top">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>
      </div>
    </main>
      <aside class="sidebar">
        <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="朱酱酱的学习博客"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/JVM/">JVM</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/JDK%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/">JDK源码</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/">多线程</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Mysql/">Mysql</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Redis/">Redis</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E8%AE%BE%E8%AE%A1%E8%80%85%E6%A8%A1%E5%BC%8F/">设计模式</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/MyBatis/">MyBatis</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/SpringMVC/">SpringMVC</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Spring/">Spring</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/SpringBoot/">SpringBoot</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">Linux</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Leetcode/">Leetcode</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E5%89%8D%E7%AB%AF/">前端</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/">网络编程</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/photoshop/">photoshop</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="http://smartzhuuu.lofter.com/" target="_blank" rel="noopener">摄影</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2020/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
      </aside>
      <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
      
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/jquery.justifiedGallery.min.js"></script>


<script src="/js/lazyload.min.js"></script>


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<script src="/js/share.js"></script>



<script src="/fancybox/jquery.fancybox.min.js"></script>




<script>
  try {
    var typed = new Typed("#subtitle", {
    strings: ['昨夜西风凋碧树。独上高楼，望尽天涯路','衣带渐宽终不悔，为伊消得人憔悴。','众里寻他千百度。蓦然回首，那人却在，灯火阑珊处。'],
    startDelay: 0,
    typeSpeed: 200,
    loop: true,
    backSpeed: 100,
    showCursor: true
    });
  } catch (err) {
  }
  
</script>




<script src="/js/tocbot.min.js"></script>

<script>
  // Tocbot_v4.7.0  http://tscanlin.github.io/tocbot/
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer:'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto',
    onClick: (e) => {
      $('.toc-link').removeClass('is-active-link');
      $(`a[href=${e.target.hash}]`).addClass('is-active-link');
      $(e.target.hash).scrollIntoView();
      return false;
    }
  });
</script>


<script>
  var ayerConfig = {
    mathjax: true
  }
</script>


<script src="/js/ayer.js"></script>


<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">


<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>



<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>


<script type="text/javascript" src="https://js.users.51.la/20544303.js"></script>
  </div>
</body>

</html>